import SparkMD5 from "spark-md5"
import compressor from "compressorjs"

//定义文件最大值 1M
const FILE_SIZE = 1024 * 1024;

//定义计算文件hash值的库
//小文件的hash库
const spark = new SparkMD5.ArrayBuffer();

//大文件的hash库
const sparkBig = new SparkMD5.ArrayBuffer();

//定义文件块
interface Chunk {
    //主要文件hash值
    hashName: string,
    //文件块
    chunk: Blob,
    //文件块hash
    chunkHash: string,
    //文件索引
    index: number,
    //是否是大文件
    isBIg: boolean
}

/**
 * 计算文件hash值
 * @param chunk 文件对象
 */
const count = (chunk:Blob) => {
    //实例化文件读取器 用于读取文件的流
    let readFile = new FileReader();
    //声明变量
    let end:string;
    //读取文件的二进制
    readFile.readAsArrayBuffer(chunk)



    return new Promise((resolve, reject) => {
        //读取器完成的回调
        readFile.onload = (e) => {
            //追加文件数据流
            spark.append(e.target.result)
            //开始计算
            end = spark.end();
            //清除缓存
            spark.reset()
            resolve(end)
        }
    });
}

/**
 * 异步函数
 * 计算大文件hash值
 * @param file 大文件
 */
const countAllFile = async (file:Blob) => {

    //存储文件块的列表
    let chunkFile :Chunk[] = [];
    //声明文件读取器 读取流
    let readFile = new FileReader();
    //创建索引
    let index = 0;

    //循环读取文件
    for (let i = 0; i < file.size; i += FILE_SIZE) {
        //Blob里面有个分割函数可以分割文件
        //函数slice(参数一，参数二) 参数一详解 ：从第几个字节开始读取 参数二详解：读取到第几个字节结束
        let chunk = file.slice(i, i + FILE_SIZE);
        //将文件块添加到数组中
        readFile.readAsArrayBuffer(chunk)
        //在前面加上 await 等 promise的完成回调
        //使用promise的resolve函数表示完成回调
        await new Promise((resolve) => {
            //文件读取器读取完成的回调
            readFile.onload = (e) => {
                //将文件块添加到sparkBig中
                sparkBig.append(e.target.result);
                //将文件块添加到spark中
                spark.append(e.target.result)
                //计算文件块的hash值，并存储到chunkHash中
                let chunkHash = spark.end()
                //清除spark的值
                spark.reset()
                //将文件块添加到chunkFile列表中
                chunkFile.push({
                    //将hashName
                    hashName: chunkHash,
                    //文件块
                    chunk: chunk,
                    //文件块的hash
                    chunkHash: chunkHash,
                    //索引
                    index: index,
                    //是文件
                    isBIg: true
                })
                //完成回调 程序继续
                resolve("")
            }
        })
        //索引增加
        index++;
    }

    //循环结束，说明大文件的所有文件数据加载到sparkBig中
    //使用end开始计算大文件的hash值
    let end = sparkBig.end();

    //将大文件的hash值添加到chunkFile列表中
    for (let i = 0; i < chunkFile.length; i++) {
        chunkFile[i].hashName = end;
    }
    //大文件分片结束
    return chunkFile
}

/**
 * 文件计算hash值主要入口
 * @param file
 */
export const fileCount = async (file:Blob):Promise<Chunk[]> => {
    //声明一个文件块列表
    let chunkFile :Chunk[] = [];

    //将file类型转换成Blob类型
    let blob = new Blob([file], {type: file.type});

    //判断文件大小是否大于1M
    //大于1M说明是大文件，使用countAllFile函数
    if (blob.size > FILE_SIZE) {
        chunkFile = await countAllFile(blob)
    } else {
        //小于1M说明是小文件，使用count函数计算文件hash值
        let s = await count(blob) + "";
        //将文件hash值添加到chunkFile列表中
        chunkFile.push({
            hashName: s,
            chunk: blob,
            chunkHash: s,
            index: 0,
            isBIg: false
        })
    }
    //将计算好的文件列表返回
    return chunkFile
}

export const zipFile = async (file:Blob) => {
    if (file.size > FILE_SIZE) {
        return await new Promise(resolve => {
            new compressor(file, {
                quality: 0.6,
                convertSize: 1024 * 1024,
                success(result) {
                    resolve(result)
                },
            })
        })
    } else {
        return file
    }
}